package com.promote.hotspot.server.hot;

import com.github.benmanes.caffeine.cache.Cache;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.promote.hotspot.server.netty.pusher.IPusher;
import com.promote.hotspot.server.rule.KeyRuleHolder;
import com.promote.hotspot.server.cache.CaffeineCacheHolder;
import com.promote.hotspot.server.tool.SlidingWindow;
import lombok.extern.slf4j.Slf4j;
import org.promote.hotspot.common.model.HotKeyModel;
import org.promote.hotspot.common.rule.KeyRule;
import org.promote.hotspot.common.tool.SystemClock;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

/**
 * 热点处理器
 *
 * @author enping.jep
 * @date 2024/01/17 15:19
 **/
@Component
@Slf4j
public class HotSpotHandler {

    private static final String SPLITER = "-";

    @Resource
    private List<IPusher> iPushers;

    @Resource(name = "hotSpotCache")
    private Cache<String, Object> hotSpotCache;

    public void handle(HotKeyModel hotKeyModel) {
        String uniqueKey = buildHotKey(hotKeyModel);
        log.info("uniqueKey == " + uniqueKey);
        //判断是不是刚热不久(5S内能取到值)，如果是，则直接返回，防止多次推送
        Object o = hotSpotCache.getIfPresent(uniqueKey);
        if (o != null) {
            log.info("o == " + o.toString());
            return;
        }
        SlidingWindow slidingWindow = checkWindow(hotKeyModel, uniqueKey);
        boolean isHot = slidingWindow.addCount(hotKeyModel.getCount());//是否是热点
        log.info("isHot ---- " + isHot);
        //如果已经热了，则推送数据
        if (isHot) {
            //如果没有触发阈值，put进缓存
//            CaffeineCacheHolder.getCache(hotKeyModel.getAppName()).put(key, slidingWindow);
            //如果触发阈值，则记录该key，防止多次推送-如果触发了阈值  5S内不会多次推送
            hotSpotCache.put(uniqueKey, 1);
            //删掉该key  <appName0  ,     cache<appName-keyType-key,SlidingWindow>   >
            CaffeineCacheHolder.getCache(hotKeyModel.getAppName()).invalidate(uniqueKey);
            //开启推送
            hotKeyModel.setCreateTime(SystemClock.now());

            //分别推送到各client和etcd
            for (IPusher pusher : iPushers) {
                pusher.push(hotKeyModel);
            }
        }
    }

    private String buildHotKey(HotKeyModel hotKeyModel) {
        //连接不同的字符串或者对象，它们中间用SPLITER分割。
        //appName-keyType-key 对客户端上报的数据进行唯一性的编码
        return Joiner.on(SPLITER).skipNulls().join(hotKeyModel.getAppName(), hotKeyModel.getKeyType(), hotKeyModel.getKey());
    }

    /**
     * 生成或返回该key的滑窗
     * <appName0  ,     cache<appName-keyType-key,SlidingWindow>   >
     * <appName0  ,     cache<appName-keyType-key,SlidingWindow1>   >
     * <appName1  ,     cache<appName-keyType-key,SlidingWindow>   >
     * <appName1  ,     cache<appName-keyType-key2,SlidingWindow2>   >
     */
    private SlidingWindow checkWindow(HotKeyModel hotKeyModel, String key) {
        //取该key的滑窗  传入mappingFunction方法，首次调用会从方法中构建，后续调用从缓存中获取
        //如果缓存中有，则直接取，没有则通过后面的function构建
        return (SlidingWindow) CaffeineCacheHolder.getCache(hotKeyModel.getAppName())
                .get(key, (Function<String, SlidingWindow>) s -> {
                    //是个新key，获取它的规则
                    KeyRule keyRule = KeyRuleHolder.getRuleByAppAndKey(hotKeyModel);
                    log.info("缓存中没有对应的value，查询keyRule == " + keyRule.toString());
                    return new SlidingWindow(keyRule.getInterval(), keyRule.getThreshold());
                });
    }
}
